home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 February: Tool Chest / Dev.CD Feb 99 TC.toast / What's New? / Development Kits / Mac OS USB v1.1f3 DDK / Examples / USBSampleStorageDriver / StorageClassShim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-12-14  |  10.4 KB  |  383 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassShim.c
  3.  
  4.     Contains:    All functions for the Sample USB Storage Class Shim
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12.  
  13. /*
  14. MacOS Headers
  15. */
  16. #include <CodeFragments.h>
  17. #include <Devices.h>
  18. #include <DriverServices.h>
  19. #include <MacTypes.h>
  20. #include <Processes.h>
  21. #include <Resources.h>
  22. #include <USB.h>
  23.  
  24.  
  25. /*
  26. Driver Headers
  27. */
  28. #include "StorageClassShim.h"
  29. #include "SampleStorageVersion.h"
  30. #include "SampleStorageDeviceID.h"
  31.  
  32.  
  33. /*
  34. Definitions, Enumerations and Typedefs used by the Shim
  35. */
  36. // Enumerations for the range of Unittable entries
  37. // in which InstallDriverFromMemory will try to install the driver
  38. enum
  39. {
  40.     kUnitTableEntryStart    = 48,
  41.     kUnitTableEntryEnd        = 127
  42. };
  43.  
  44. // These are the structures and definitions used for the Refnum Association array.
  45. // This array handles associating USBDeviceRefs with UnitTable Driver refnums, so
  46. // Drivers can be removed from the UnitTable when the drive is removed.
  47. enum
  48. {
  49.     kMaxNumberOfDrives = 50
  50. };
  51.  
  52. struct RefnumAssociation
  53. {
  54.     USBDeviceRef     theDevRef;
  55.     DriverRefNum    drvrRefNum;
  56. };
  57.  
  58. typedef struct RefnumAssociation    RefnumAssociation;
  59.  
  60.  
  61. /*
  62. Global variables used by the Shim
  63. */
  64. static RefnumAssociation                    theRefAssoc[kMaxNumberOfDrives];
  65. static Boolean                                 shimInFile;
  66. static FSSpec                                shimFSSpec;
  67. static TimerID                                gGiveTimeTimer = nil;
  68. static DriverRefNum                            gNewDrvrRef = 0;
  69. static USBDeviceNotificationParameterBlock     gpb;
  70.  
  71.  
  72. /* 
  73. Prototypes for internal support functions
  74. */
  75. static OSStatus     GiveTimeSecondaryInterrupt( void *p1, void *p2);
  76. static OSStatus     ShimOpenDriver(USBDeviceRef theDevRef);
  77. static OSStatus     ShimCloseDriver(USBDeviceRef theDevRef);
  78.  
  79.  
  80. /*
  81. Exported Functions for the Code Fragment Manager
  82. */
  83. // This routine gets called by the Code Fragment Manager when the shim's code fragment is loaded
  84. OSErr CFragInitRoutine(CFragInitBlockPtr initBlkPtr)
  85. {
  86.     shimInFile = false;
  87.     
  88.     if (CFragHasFileLocation(initBlkPtr->fragLocator.where))
  89.     {
  90.         shimInFile = true;
  91.         shimFSSpec = *(initBlkPtr->fragLocator.u.onDisk.fileSpec);            // save the FSSpec, in case we need it later
  92.     }
  93.     return noErr;
  94. }
  95.  
  96.  
  97. /*
  98. Functions Exported for the USB Expert
  99. */
  100. // This routine is exported for use by the USB Expert to initialize the shim when
  101. // the expert loads it
  102. OSStatus USBShim(void)
  103. {
  104.     OSStatus status = noErr;
  105.  
  106.     // Clear out the Association array
  107.     BlockZero((Ptr) theRefAssoc, sizeof(RefnumAssociation) * kMaxNumberOfDrives);
  108.  
  109.     // Setup notification for Vendor Specific Devices
  110.     gpb.usbDeviceNotification     = kNotifyAnyEvent;            // tell me about everything
  111.     gpb.usbClass                 = kUSBAnyClass;
  112.     gpb.usbSubClass             = kUSBAnySubClass;
  113.     gpb.usbProtocol             = kUSBAnyProtocol;
  114.     gpb.usbVendor                 = kDriverVendorID;            // Notify me of my specific Vendor ID
  115.     gpb.usbProduct                 = kDriverProductID;            // Notify me of my specific Product ID
  116.     gpb.result                     = noErr;
  117.     gpb.callback                 = (USBDeviceNotificationCallbackProcPtr) &myNotificationCallback;        
  118.     gpb.refcon                     = nil;
  119.      USBInstallDeviceNotification (&gpb);    
  120.  
  121.     return status;
  122. }
  123.  
  124. // This routine is exported for use by the USB Expert to terminate the shim before it gets removed
  125. void USBShimTermination(void)
  126. {
  127.     // Remove the device notofication
  128.     USBRemoveDeviceNotification(gpb.token); 
  129. }
  130.  
  131. // This function is passed to the USB Expert for device add/remove notification
  132. void myNotificationCallback(USBDeviceNotificationParameterBlock *pb)
  133. {
  134.     switch(pb->usbDeviceNotification)        // why were we notified?
  135.     {
  136.         case kNotifyAddDevice:                // because mass storage device appeared
  137.         case kNotifyAddInterface:            // because mass storage interface appeared
  138.         {    
  139.             ShimOpenDriver(pb->usbDeviceRef); 
  140.         }
  141.         break;
  142.             
  143.         case kNotifyRemoveDevice:            // because a mass storage device or interface disappeared
  144.         case kNotifyRemoveInterface:        // because a mass storage device or interface disappeared
  145.         {
  146.             ShimCloseDriver(pb->usbDeviceRef);
  147.         }
  148.         break;
  149.             
  150.         default:
  151.             break;
  152.     }
  153. }
  154.  
  155. /*
  156. Functions Internal to the Shim
  157. */
  158. // Function that will load and install the driver for a new device.
  159. // As stated in the USB DDK API, the notification always occurs at task time.
  160. OSStatus ShimOpenDriver(USBDeviceRef theDevRef)
  161. {
  162.     OSStatus            theErr = noErr;
  163.     Handle                hDrvrResource;
  164.     short                loopCount;
  165.     THz                    currentZone;
  166.     
  167.     // We have been notified of a new device, check to see if we already 
  168.     // have installed a driver the USB Device Ref.
  169.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  170.     {
  171.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  172.         {
  173.             // Driver has already been installed for this USBDeviceRef,
  174.             // Nothing else needs to be done, report back noErr to the notifier
  175.             return noErr;
  176.         }
  177.     }
  178.  
  179.     // No driver has been loaded for this USBDeviceRef, find the driver's 'ndrv' resource        
  180.     if (shimInFile)
  181.     {
  182.         short    oldResFileID = 0, 
  183.                 myResFileID = 0;
  184.  
  185.         SetResLoad(true);
  186.         oldResFileID = CurResFile();                                    // get the current resource file ID
  187.         myResFileID = FSpOpenResFile(&shimFSSpec, fsRdPerm);
  188.         UseResFile(myResFileID);                                        // point at the shim file
  189.  
  190.         currentZone = GetZone ();
  191.         SetZone ( SystemZone() );
  192.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  193.         DetachResource(hDrvrResource);                                    // Detach the resource so it hangs around in the system heap
  194.         SetZone (currentZone);
  195.  
  196.         UseResFile(oldResFileID);                                        // point at the original file
  197.         CloseResFile(myResFileID);                                        // Make sure the resource file is closed
  198.     }
  199.     else
  200.     {
  201.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  202.     }
  203.     
  204.     // We have found the driver's 'ndrv' resource, install it into the UnitTable
  205.     if (hDrvrResource)
  206.     {
  207.         long                drvrSize;
  208.         Ptr                    pDrvrInMemory;
  209.         DriverRefNum        drvrRefNum;
  210.         void                *pTheStorageClassDispatchTable;
  211.         CFragSymbolClass    symClass;
  212.         CFragConnectionID    connID;
  213.         short                loopCount;
  214.  
  215.         // Lock the Driver Resource in memory
  216.         HLock(hDrvrResource);
  217.  
  218.         // Get the resource information needed to install the Driver
  219.         pDrvrInMemory = *hDrvrResource;
  220.         drvrSize = GetHandleSize(hDrvrResource);
  221.  
  222.         // Install the driver
  223.         theErr = InstallDriverFromMemory(pDrvrInMemory, drvrSize, nil, (RegEntryIDPtr) nil, kUnitTableEntryStart, kUnitTableEntryEnd, &drvrRefNum);
  224.         if ( theErr != noErr )
  225.         {
  226.             // The driver could not be loaded, the shim will return the error and abort the driver load
  227.             return theErr;
  228.         }
  229.  
  230.         gNewDrvrRef = drvrRefNum;
  231.         
  232.         // Save the Driver refnum to remove the driver when the remove notification is recieved
  233.         for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++ )
  234.         {
  235.             if (( theRefAssoc[loopCount].theDevRef == 0 ) && (theRefAssoc[loopCount].drvrRefNum == 0) )
  236.             {
  237.                 theRefAssoc[loopCount].theDevRef = theDevRef;
  238.                 theRefAssoc[loopCount].drvrRefNum = drvrRefNum;
  239.                 break;
  240.             }
  241.         }
  242.  
  243.         // If the array is already full, this drive will not be added to the array.  This means that the Shim will never
  244.         // remove the driver it the drive is detached.  This will only happen if more than kMaxNumberOfDrives are attached
  245.         // at any one time.
  246.  
  247.         // Get the Storage class dispatch table 
  248.         USBGetDriverConnectionID(&theDevRef, &connID);
  249.         currentZone = GetZone ();
  250.         SetZone ( SystemZone() );
  251.         
  252.         theErr = FindSymbol(connID, "\pTheStorageClassDispatchTable", (Ptr *)&pTheStorageClassDispatchTable, &symClass);
  253.         SetZone (currentZone);
  254.  
  255.         // If no error occured, pass the dispatch table pointer to the Driver
  256.         if (theErr == noErr)            
  257.         {
  258.             // Since we know that the Unit Table driver doesn't make any system calls
  259.             // for this control code, we can make the call synchronously.
  260.             theErr = Control(drvrRefNum, 500, &pTheStorageClassDispatchTable);
  261.         }
  262.         
  263.         if ( theErr != noErr )
  264.         {
  265.             // if an error occurs, we should remove the driver from the unittable
  266.             ShimCloseDriver(theDevRef);
  267.         }
  268.     }
  269.     
  270.     return theErr;
  271. }
  272.  
  273. OSStatus ShimCloseDriver(USBDeviceRef theDevRef)
  274. {
  275.     OSStatus            theErr = noErr;
  276.     short                loopCount;
  277.  
  278.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  279.     {
  280.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  281.         {
  282.             if(theRefAssoc[loopCount].drvrRefNum != 0)
  283.             {
  284.                 VCBPtr vol;
  285.                 
  286.                 vol = (VCBPtr) (GetVCBQHdr())->qHead;
  287.                 
  288.                 while (vol)
  289.                 {
  290.                     // Check to see if this volume belongs to the driver
  291.                     // that is being removed
  292.                     if( vol->vcbDRefNum == theRefAssoc[loopCount].drvrRefNum)
  293.                     {
  294.                         IOParam     offlinePB;
  295.                         OSErr        err;
  296.                         
  297.                         BlockZero(&offlinePB, sizeof(IOParam));
  298.                         offlinePB.ioCompletion = nil;    
  299.                         offlinePB.ioResult = noErr;    
  300.                         offlinePB.ioNamePtr = nil;    
  301.                         offlinePB.ioVRefNum = vol->vcbVRefNum;    
  302.                         
  303.                         err = PBUnmountVol((ParamBlockRec *) &offlinePB);
  304.                         if(err == fBsyErr)
  305.                         {
  306.                             err = PBEject((ParamBlockRec *) &offlinePB);
  307.                             gNewDrvrRef = 0;
  308.                         }
  309.                     }
  310.                     
  311.                     vol = (VCBPtr) vol->qLink;
  312.                 }
  313.                 
  314.                 theErr = RemoveDriver(theRefAssoc[loopCount].drvrRefNum, false);
  315.                 theRefAssoc[loopCount].theDevRef = 0;
  316.                 theRefAssoc[loopCount].drvrRefNum = 0;
  317.                 
  318.                 if(gNewDrvrRef == 0)
  319.                 {
  320.                     OSStatus             status;
  321.                     AbsoluteTime        theWait;
  322.                 
  323.                     theWait = DurationToAbsolute(durationSecond);
  324.                     theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  325.                     status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  326.                 }
  327.             }
  328.             
  329.             return theErr;
  330.         }
  331.     }
  332.     
  333.     return theErr;
  334. }
  335.  
  336. // This is the interrupt timer to provide the USB Manager to get time if a device was
  337. // removed with a volume in use.
  338. // The shortcomings of this implementation are:
  339. //         1. This will only work if a single in use device is removed.  Multiple devices can cause
  340. //            problems where the shim could get about confused whether the device was reattached.
  341. //        2. This will only work if the device that is removed has only one busy volume mounted.
  342. //            More than one volume may cause the shim to get confused about whether the volume was remounted
  343. OSStatus GiveTimeSecondaryInterrupt( void *p1, void *p2)
  344. {
  345. #pragma unused ( p1, p2 )
  346.     Boolean     DrvrVolumeFound = false;
  347.  
  348.     gGiveTimeTimer = nil;
  349.  
  350.     if(gNewDrvrRef != 0)
  351.     {
  352.         VCBPtr vol;
  353.         
  354.         vol = (VCBPtr) (GetVCBQHdr())->qHead;
  355.         
  356.         while (vol)
  357.         {
  358.             // Check to see if this volume belongs to the driver
  359.             // that is being removed
  360.             if( vol->vcbDRefNum == gNewDrvrRef)
  361.             {
  362.                 DrvrVolumeFound = true;
  363.                 break;
  364.             }
  365.             
  366.             vol = (VCBPtr) vol->qLink;
  367.         }
  368.     }
  369.  
  370.     if( DrvrVolumeFound == false )
  371.     {
  372.         OSStatus             status;
  373.         AbsoluteTime        theWait;
  374.  
  375.         SystemTask();
  376.         theWait = DurationToAbsolute(durationSecond);
  377.         theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  378.         status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  379.     }
  380.                 
  381.     return noErr;
  382. }
  383.